import { defineStore } from 'pinia';
import * as JDFModels from '@/models/jdf';
type FlowNode = JDFModels.FlowNode;
type FlowEdge = JDFModels.FlowEdge;
type ParseResult = JDFModels.ParseResult;
import { parseWithSelectedParser } from '@/parsers';
import { applyDagreLayout } from '@/utils/layoutUtils';
import { formatXmlWithOptions } from '@/utils/formatterUtils';
import { useSettingsStore } from './settingsStore'; // Keep settings store dependency
import { generateXmlFromFlow } from '@/utils/xmlGenerator';
import { v4 as uuidv4 } from 'uuid';
import { ElMessage, ElMessageBox } from 'element-plus'; // Keep Element Plus components

// Import the new file manager store
import { useFileManagerStore } from './fileManagerStore';

// Type for file info passed from fileManagerStore
interface CurrentFileInfo {
    uuid: string | null;
    name: string;
    lastModified: Date | null;
}

// Helper function for confirmation (can be kept here or moved if only fileManager uses it)
async function confirm(message: string): Promise<boolean> {
    try {
        await ElMessageBox.confirm(message, '确认操作', {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning',
        });
        return true;
    } catch (action) {
        if (action === 'cancel') {
            ElMessage.info('操作已取消');
        }
        return false;
    }
}


// Rename the store ID and export constant
export const useEditorStore = defineStore('editor', {
    state: () => ({
        rawContent: '' as string,
        parsedDocument: null as Document | object | null | undefined,
        drawerNodeId: null as string | null,
        visualSelectedNodeId: null as string | null,
        flowElements: {
            nodes: [] as FlowNode[],
            edges: [] as FlowEdge[]
        },
        // Renamed fileInfo and simplified structure
        currentFileInfo: {
            uuid: null,
            name: '',
            lastModified: null
        } as CurrentFileInfo,
        isLoading: false as boolean, // For parsing/layout operations
        error: null as string | null, // For editor/parsing/layout errors
        isDirty: false as boolean, // Tracks unsaved changes relative to loaded state
    }),

    getters: {
        /** Checks if a file is currently loaded in the editor */
        hasLoadedFile(state): boolean {
            return state.currentFileInfo.uuid !== null;
        },
        /** Checks if there is any content in the editor */
        hasContent(state): boolean {
            // Content exists if rawContent is not empty OR a file is technically loaded (even if empty)
            return state.rawContent.length > 0 || state.currentFileInfo.uuid !== null;
        },
        /** Gets the currently selected flow node data */
        selectedFlowNode(state): FlowNode | undefined {
            return state.flowElements.nodes.find(node => node.id === state.visualSelectedNodeId);
        },
        /** Gets the drawer node data */
        drawerNodeData(state): FlowNode | undefined {
            return state.flowElements.nodes.find(node => node.id === state.drawerNodeId);
        }
    },

    actions: {
        // --- Core Editor and Visualization Actions ---

        /** Clears the editor state */
        clearEditorState() {
            this.rawContent = '';
            this.parsedDocument = null;
            this.flowElements = { nodes: [], edges: [] };
            this.currentFileInfo = { uuid: null, name: '', lastModified: null };
            this.drawerNodeId = null;
            this.visualSelectedNodeId = null;
            this.isLoading = false;
            this.error = null;
            this.isDirty = false;
            console.log("[Editor] State cleared.");
        },

        /** [NEW] Loads content and file info into the editor state. Called by fileManagerStore. */
        async loadContent(content: string, fileInfo: CurrentFileInfo) {
            // Optional: Check for unsaved changes before loading (might be redundant if fileManager already checked)
            // if (this.isDirty) {
            //     const confirmed = await confirm('当前编辑器有未保存的更改，确定要加载新文件并丢弃更改吗？');
            //     if (!confirmed) {
            //         // How to signal cancellation back to fileManager? Or maybe fileManager handles this fully.
            //         console.log("[Editor loadContent] Load cancelled due to unsaved changes.");
            //         return false;
            //     }
            // }

            this.isLoading = true;
            this.error = null;
            try {
                console.log(`[Editor loadContent] Loading content for ${fileInfo.name} (${fileInfo.uuid})`);
                // Update file info first
                this.currentFileInfo = { ...fileInfo };

                // Parse and layout the loaded content
                // This internal action now only handles the parsing/layout part
                await this._parseAndLayoutContent(content);

                if (this.error) {
                    // Error is already set by _parseAndLayoutContent
                    console.error(`[Editor loadContent] Error during parsing/layout for ${fileInfo.name}: ${this.error}`);
                    // Even if parsing fails, keep the file info loaded, but maybe clear flow elements?
                    // this.flowElements = { nodes: [], edges: [] }; // Already handled by _parseAndLayoutContent
                    throw new Error(this.error); // Re-throw to signal failure
                }

                this.isDirty = false; // Reset dirty flag after successful load
                console.log(`[Editor loadContent] Successfully loaded and parsed ${fileInfo.name}`);
                return true; // Indicate success

            } catch (err: any) {
                this.error = `加载文件内容失败: ${err.message}`;
                // Don't clear file info here, maybe the file is loaded but content is invalid
                // this.clearEditorState(); // Avoid clearing state totally on parse failure
                this.flowElements = { nodes: [], edges: [] }; // Clear visualization on error
                this.rawContent = content; // Keep raw content even if parse fails
                this.isDirty = false; // Content loaded, even if invalid, it's not "dirty" yet
                console.error('[Editor loadContent] Error:', err);
                return false; // Indicate failure
            } finally {
                this.isLoading = false;
            }
        },


        /** [INTERNAL] Parses content and updates flow elements. Called internally by loadContent etc. */
        async _parseAndLayoutContent(content: string) {
            // No loading state management here, handled by caller
            this.error = null;
            this.rawContent = content; // Update raw content
            try {
                const result = await parseWithSelectedParser(content)
                if (result.success) {
                    const settingsStore = useSettingsStore()
                    // Ensure rankdir is valid, default to 'TB' if not found or invalid
                    const rankdir = ['TB', 'LR'].includes(settingsStore.dagre.rankdir) ? settingsStore.dagre.rankdir : 'TB';
                    const layoutResult = applyDagreLayout(result.nodes, result.edges, rankdir);

                    this.parsedDocument = result.document
                    this.flowElements.nodes = layoutResult.nodes
                    this.flowElements.edges = layoutResult.edges
                    console.log("[Editor _parseAndLayoutContent] Parsing and layout successful.");
                } else {
                    this.error = result.error || '解析失败';
                    this.flowElements = { nodes: [], edges: [] };
                    this.parsedDocument = null;
                    console.error("[Editor _parseAndLayoutContent] Parsing failed:", this.error);
                }
            } catch (err: any) {
                this.error = `处理内容时出错: ${err.message}`;
                this.flowElements = { nodes: [], edges: [] };
                this.parsedDocument = null;
                console.error('[Editor _parseAndLayoutContent] Error:', err);
            }
            // No finally block needed
        },

        /** Called when the editor content changes */
        handleEditorChange(newContent: string) {
            // Prevent marking dirty if content is programmatically set during loading/saving
            if (this.isLoading) return;

            if (newContent !== this.rawContent) {
                this.rawContent = newContent;
                // Only mark as dirty if a file is actually loaded.
                // Changes to a new, unsaved file don't make it "dirty" relative to a saved state.
                if (this.hasLoadedFile) {
                    this.isDirty = true;
                }
            }
        },

        // --- Node Selection/Interaction Actions (Keep as is, mostly) ---
        selectNodeForDrawer(nodeId: string | null): void {
            this.drawerNodeId = nodeId;
            this.visualSelectedNodeId = nodeId;
        },

        selectNodeVisually(nodeId: string | null): void {
            this.visualSelectedNodeId = nodeId;
        },

        /** Formats XML and potentially triggers a save */
        async formatXml(): Promise<string | undefined> {
            if (!this.rawContent) return;

            const settingsStore = useSettingsStore();
            const options = settingsStore.formattingOptions;
            let formatted: string | undefined = undefined;

            try {
                formatted = formatXmlWithOptions(this.rawContent, options);

                if (formatted !== this.rawContent) {
                    this.rawContent = formatted; // Update content
                    this.error = null;
                    if (this.hasLoadedFile) {
                        this.isDirty = true; // Mark as dirty
                        // Request save after formatting
                        await this.requestSave();
                    }
                }
                return formatted;
            } catch (err: any) {
                console.error('[Editor formatXml] Error:', err);
                this.error = `格式化时发生意外错误: ${err.message}`;
                ElMessage.error(this.error);
                return this.rawContent; // Return original content on error
            }
        },

        // --- Node Expansion/Collapse Actions (Keep as is) ---
        toggleNodeExpansion(nodeId: string): void {
            const node = this.flowElements.nodes.find(n => n.id === nodeId);
            if (!node || !node.data.hasChildren) return;

            node.data.isExpanded = !node.data.isExpanded;
            const isNowExpanded = node.data.isExpanded;

            const findAllDescendantIds = (startNodeId: string): { nodeIds: Set<string>, edgeIds: Set<string> } => {
                const nodeIds = new Set<string>();
                const edgeIds = new Set<string>();
                const queue: string[] = [];
                const visitedEdges = new Set<string>();

                const directChildrenEdges = this.flowElements.edges.filter(e => e.source === startNodeId);
                directChildrenEdges.forEach(edge => {
                    if (!visitedEdges.has(edge.id)) {
                        edgeIds.add(edge.id);
                        visitedEdges.add(edge.id);
                        const targetId = edge.target;
                        nodeIds.add(targetId);
                        queue.push(targetId);
                    }
                });

                while (queue.length > 0) {
                    const currentId = queue.shift()!;
                    const childrenEdges = this.flowElements.edges.filter(e => e.source === currentId);

                    childrenEdges.forEach(edge => {
                        if (!visitedEdges.has(edge.id)) {
                            edgeIds.add(edge.id);
                            visitedEdges.add(edge.id);
                            const targetId = edge.target;
                            if (!nodeIds.has(targetId)) {
                                nodeIds.add(targetId);
                                queue.push(targetId);
                            }
                        }
                    });
                }
                return { nodeIds, edgeIds };
            };

            const allDescendants = findAllDescendantIds(nodeId);

            this.flowElements.nodes.forEach(n => {
                if (allDescendants.nodeIds.has(n.id)) {
                    if (isNowExpanded) {
                        const edgeToThisNode = this.flowElements.edges.find(e => e.target === n.id);
                        if (edgeToThisNode?.source === nodeId) {
                            n.hidden = false;
                            if (!n.data.isExpanded && n.data.hasChildren) {
                                const grandChildren = findAllDescendantIds(n.id);
                                this.flowElements.nodes.forEach(gcNode => {
                                    if (grandChildren.nodeIds.has(gcNode.id)) gcNode.hidden = true;
                                });
                                this.flowElements.edges.forEach(gcEdge => {
                                    if (grandChildren.edgeIds.has(gcEdge.id)) gcEdge.hidden = true;
                                });
                            }
                        } else {
                            n.hidden = true;
                        }
                    } else {
                        n.hidden = true;
                    }
                }
            });

            this.flowElements.edges.forEach(e => {
                if (allDescendants.edgeIds.has(e.id)) {
                    if (isNowExpanded) {
                        const targetNode = this.flowElements.nodes.find(n => n.id === e.target);
                        if (e.source === nodeId && !targetNode?.hidden) {
                            e.hidden = false;
                        } else {
                            e.hidden = true;
                        }
                    } else {
                        e.hidden = true;
                    }
                }
            });

            this.flowElements.nodes = [...this.flowElements.nodes];
            this.flowElements.edges = [...this.flowElements.edges];
        },

        expandAllChildren(nodeId: string): void {
            const node = this.flowElements.nodes.find(n => n.id === nodeId);
            if (!node || !node.data.hasChildren) return;

            const nodesToUpdate = new Set<string>();
            const edgesToUpdate = new Set<string>();
            const queue: string[] = [nodeId];

            while (queue.length > 0) {
                const currentId = queue.shift()!;
                const currentNode = this.flowElements.nodes.find(n => n.id === currentId);

                if (currentNode && currentNode.data.hasChildren) {
                    if (!currentNode.data.isExpanded) {
                        nodesToUpdate.add(currentId);
                    }
                    const childrenEdges = this.flowElements.edges.filter(e => e.source === currentId);
                    childrenEdges.forEach(edge => {
                        edgesToUpdate.add(edge.id);
                        const targetId = edge.target;
                        nodesToUpdate.add(targetId);
                        queue.push(targetId);
                    });
                }
            }

            this.flowElements.nodes.forEach(n => {
                if (nodesToUpdate.has(n.id)) {
                    n.hidden = false;
                    if (n.data.hasChildren) {
                        n.data.isExpanded = true;
                    }
                }
            });
            this.flowElements.edges.forEach(e => {
                if (edgesToUpdate.has(e.id)) {
                    e.hidden = false;
                }
            });

            this.flowElements.nodes = [...this.flowElements.nodes];
            this.flowElements.edges = [...this.flowElements.edges];
        },

        collapseAllChildren(nodeId: string): void {
            const node = this.flowElements.nodes.find(n => n.id === nodeId);
            if (!node || !node.data.hasChildren) return;

            const nodesToUpdate = new Set<string>();
            const edgesToUpdate = new Set<string>();
            const queue: string[] = [];

            if (node.data.isExpanded) {
                nodesToUpdate.add(nodeId);
            }

            const findDescendants = (startNodeId: string) => {
                const directChildrenEdges = this.flowElements.edges.filter(e => e.source === startNodeId);
                directChildrenEdges.forEach(edge => {
                    edgesToUpdate.add(edge.id);
                    const targetId = edge.target;
                    nodesToUpdate.add(targetId);
                    queue.push(targetId);
                });

                while (queue.length > 0) {
                    const currentId = queue.shift()!;
                    const childrenEdges = this.flowElements.edges.filter(e => e.source === currentId);
                    childrenEdges.forEach(edge => {
                        if (!edgesToUpdate.has(edge.id)) {
                            edgesToUpdate.add(edge.id);
                            const targetId = edge.target;
                            if (!nodesToUpdate.has(targetId)) {
                                nodesToUpdate.add(targetId);
                                queue.push(targetId);
                            }
                        }
                    });
                }
            };

            findDescendants(nodeId);

            this.flowElements.nodes.forEach(n => {
                if (nodesToUpdate.has(n.id)) {
                    if (n.id === nodeId) {
                        n.data.isExpanded = false;
                    } else {
                        n.hidden = true;
                    }
                }
            });
            this.flowElements.edges.forEach(e => {
                if (edgesToUpdate.has(e.id)) {
                    e.hidden = true;
                }
            });

            this.flowElements.nodes = [...this.flowElements.nodes];
            this.flowElements.edges = [...this.flowElements.edges];
        },


        // --- Node Data Modification Actions (Need save integration) ---
        /** Updates a node's attribute and triggers save */
        async updateNodeAttribute(payload: { nodeId: string; oldName: string; newName: string; newValue: string }): Promise<void> {
            const node = this.flowElements.nodes.find(n => n.id === payload.nodeId);
            if (!node || !node.data.attributes) return;

            let attributeFound = false;
            for (let i = 0; i < node.data.attributes.length; i++) {
                if (node.data.attributes[i].name === payload.oldName) {
                    node.data.attributes[i].name = payload.newName;
                    node.data.attributes[i].value = payload.newValue;
                    attributeFound = true;
                    break;
                }
            }

            if (!attributeFound) {
                console.warn(`Attribute "${payload.oldName}" not found on node "${payload.nodeId}" for update.`);
                return;
            }

            // Mark dirty and request save
            if (this.hasLoadedFile) {
                this.isDirty = true;
                await this.requestSave(); // Request save after modification
            }

            // Trigger reactivity
            node.data = { ...node.data };
        },

        /** Adds an attribute to a node and triggers save */
        async addAttribute(payload: { nodeId: string; name: string; value: string }): Promise<void> {
            const node = this.flowElements.nodes.find(n => n.id === payload.nodeId);
            if (!node) return;

            if (!node.data.attributes) {
                node.data.attributes = [];
            }

            const existingAttr = node.data.attributes.find((attr: { name: string; value: string }) => attr.name === payload.name);
            if (existingAttr) {
                console.warn(`Attribute "${payload.name}" already exists on node "${payload.nodeId}". Cannot add duplicate.`);
                this.error = `属性 "${payload.name}" 已存在。`;
                ElMessage.error(this.error);
                return;
            }

            node.data.attributes.push({ name: payload.name, value: payload.value });

            if (this.hasLoadedFile) {
                this.isDirty = true;
                await this.requestSave();
            }

            node.data = { ...node.data };
            this.error = null;
        },

        /** Deletes an attribute from a node and triggers save */
        async deleteAttribute(payload: { nodeId: string; attributeName: string }): Promise<void> {
            const node = this.flowElements.nodes.find(n => n.id === payload.nodeId);
            if (!node || !node.data.attributes) return;

            const initialLength = node.data.attributes.length;
            node.data.attributes = node.data.attributes.filter(
                attr => attr.name !== payload.attributeName
            );

            if (node.data.attributes.length < initialLength) {
                if (this.hasLoadedFile) {
                    this.isDirty = true;
                    await this.requestSave();
                }
                node.data = { ...node.data };
            } else {
                console.warn(`Attribute "${payload.attributeName}" not found on node "${payload.nodeId}" for deletion.`);
            }
        },

        /** Deletes a node and its descendants from flow and triggers save */
        async deleteNodeWithDescendants(nodeIdToDelete: string): Promise<void> {
            const nodeToDelete = this.flowElements.nodes.find(n => n.id === nodeIdToDelete);
            if (!nodeToDelete) return;

            const nodesToRemove = new Set<string>();
            const edgesToRemove = new Set<string>();
            const queue: string[] = [nodeIdToDelete];
            nodesToRemove.add(nodeIdToDelete);

            while (queue.length > 0) {
                const currentId = queue.shift()!;
                const outgoingEdges = this.flowElements.edges.filter(e => e.source === currentId);
                outgoingEdges.forEach(edge => {
                    if (!edgesToRemove.has(edge.id)) {
                        edgesToRemove.add(edge.id);
                        if (!nodesToRemove.has(edge.target)) {
                            nodesToRemove.add(edge.target);
                            queue.push(edge.target);
                        }
                    }
                });
                const incomingEdges = this.flowElements.edges.filter(e => e.target === currentId);
                incomingEdges.forEach(edge => edgesToRemove.add(edge.id));
            }

            const originalNodeCount = this.flowElements.nodes.length;
            this.flowElements.nodes = this.flowElements.nodes.filter(n => !nodesToRemove.has(n.id));
            this.flowElements.edges = this.flowElements.edges.filter(e => !edgesToRemove.has(e.id));

            if (this.flowElements.nodes.length < originalNodeCount) {
                if (this.hasLoadedFile) {
                    this.isDirty = true;
                    await this.requestSave(); // Save after deletion
                }

                if (this.visualSelectedNodeId && nodesToRemove.has(this.visualSelectedNodeId)) {
                    this.visualSelectedNodeId = null;
                }
                if (this.drawerNodeId && nodesToRemove.has(this.drawerNodeId)) {
                    this.drawerNodeId = null;
                }
                // No layout update needed? Vue Flow should handle removal.
            } else {
                console.warn(`Node "${nodeIdToDelete}" or descendants not found for deletion.`);
            }
        },

        /** Adds a child node to a parent node and triggers save */
        async addChildNode(payload: { parentId: string; newNodeName: string }): Promise<void> {
            const parentNode = this.flowElements.nodes.find(n => n.id === payload.parentId);
            if (!parentNode) {
                console.error(`Add child failed: Parent node ${payload.parentId} not found.`);
                ElMessage.error(`添加子节点失败：未找到父节点 ${payload.parentId}`);
                return;
            }

            let newId = uuidv4();
            while (this.flowElements.nodes.some(n => n.id === newId)) { newId = uuidv4(); }
            let newEdgeId = `e-${payload.parentId}-${newId}`;
            while (this.flowElements.edges.some(e => e.id === newEdgeId)) { newEdgeId = `e-${payload.parentId}-${newId}-${Math.random().toString(16).slice(2, 8)}`; }

            const newNodeData = {
                nodeName: payload.newNodeName,
                attributes: [],
                textContent: '',
                hasChildren: false,
                isExpanded: true,
                level: (parentNode.data.level ?? 0) + 1,
                childrenIds: [],
            };

            if (!parentNode.data.childrenIds) parentNode.data.childrenIds = [];
            parentNode.data.childrenIds.push(newId);
            parentNode.data.hasChildren = true;

            const newNode: FlowNode = {
                id: newId,
                position: { x: parentNode.position.x, y: parentNode.position.y + 100 },
                type: 'jdf',
                data: { ...newNodeData },
                parentId: payload.parentId,
                hidden: parentNode.hidden || !parentNode.data.isExpanded,
            };

            const newEdge: FlowEdge = {
                id: newEdgeId,
                source: payload.parentId,
                target: newId,
                type: 'smoothstep',
                hidden: newNode.hidden
            };

            this.flowElements.nodes.push(newNode);
            this.flowElements.edges.push(newEdge);

            if (this.hasLoadedFile) {
                this.isDirty = true;
                await this.requestSave(); // Save after adding
            }

            // Trigger layout update after adding node
            this._triggerReactivityAndUpdateLayout(true);
        },

        // --- Actions for Syncing Editor and Visualizer (Need save integration) ---

        /** Generates XML from flow, updates editor content, and saves */
        async syncVisualChangesToEditor(): Promise<boolean> {
            console.log("[Editor] Syncing visual changes to editor...");
            if (!this.hasLoadedFile) {
                console.warn("[Editor] Cannot sync to editor: No file is currently loaded.");
                this.error = "无法同步：没有加载文件";
                ElMessage.error(this.error);
                return false;
            }

            try {
                // 1. Generate XML from current flow state
                const visibleNodes = this.flowElements.nodes.filter(n => !n.hidden);
                const visibleEdges = this.flowElements.edges.filter(e => !e.hidden);
                const visibleEdgeTargets = new Set(visibleEdges.map(e => e.target));
                const potentialRoots = visibleNodes.filter(n => !visibleEdgeTargets.has(n.id));

                if (potentialRoots.length === 0 && visibleNodes.length > 0) {
                    console.warn("[Editor] Cannot determine root node among visible elements for XML generation.");
                    this.error = "无法确定根节点以生成 XML";
                    ElMessage.error(this.error);
                    return false;
                } else if (potentialRoots.length === 0 && visibleNodes.length === 0) {
                    this.error = "无法生成 XML：没有可见节点。";
                    ElMessage.error(this.error);
                    return false;
                }
                const rootId = potentialRoots[0]?.id;
                if (!rootId && visibleNodes.length > 0) {
                    console.error("[Editor] Logical error: Visible nodes exist but no root ID found.");
                    this.error = "无法确定根节点";
                    ElMessage.error(this.error);
                    return false;
                }

                const generatedXml = generateXmlFromFlow(visibleNodes, visibleEdges, rootId!);
                console.log("[Editor] XML generated from visual state.");

                // 2. Check if content actually changed
                if (generatedXml !== this.rawContent) {
                    console.log("[Editor] Visual state differs from rawContent. Updating rawContent.");
                    this.rawContent = generatedXml; // Update rawContent
                    this.isDirty = true; // Mark as dirty

                    // 3. Request save for the new rawContent
                    const saved = await this.requestSave(); // This calls fileManagerStore
                    if (saved) {
                        console.log("[Editor] Sync to editor successful and saved.");
                        ElMessage.success("已同步可视化更改到编辑器并保存。");
                        // isDirty should be false now after successful save notification
                        return true;
                    } else {
                        console.error("[Editor] Sync to editor updated content, but saving failed.");
                        // Error message should be set by fileManagerStore or requestSave
                        // Keep isDirty = true because save failed
                        return false;
                    }
                } else {
                    console.log("[Editor] No effective changes detected from visual state. Sync skipped.");
                    // If content matches, ensure isDirty is false (might have been true from a previous failed save)
                    this.isDirty = false;
                    this.error = null;
                    ElMessage.info("可视化界面与编辑器内容一致，无需同步。");
                    return true;
                }
            } catch (err: any) {
                this.error = `同步到编辑器时出错: ${err.message}`;
                console.error('[Editor syncVisualChangesToEditor] Error:', err);
                ElMessage.error(this.error);
                // Don't change isDirty on error
                return false;
            }
        },

        /** Reparses editor content, updates visualizer, potentially saves first */
        async applyEditorChangesToVisualizer(): Promise<boolean> {
            console.log("[Editor] Applying editor changes to visualizer...");
            if (!this.hasLoadedFile && this.rawContent.length === 0) {
                console.warn("[Editor] Cannot apply to visualizer: No file loaded and editor is empty.");
                this.error = "无法应用：没有加载文件且编辑器为空。";
                ElMessage.error(this.error);
                return false;
            }

            // 1. Save current editor content *first* if it's dirty
            if (this.isDirty && this.hasLoadedFile) {
                console.log("[Editor applyEditorChangesToVisualizer] Editor is dirty, saving changes first...");
                const saved = await this.requestSave();
                if (!saved) {
                    console.error("[Editor applyEditorChangesToVisualizer] Apply aborted: Failed to save current editor changes before parsing.");
                    // Error message already set by requestSave/fileManager
                    ElMessage.error("应用失败：无法保存当前编辑器更改。");
                    return false; // Abort if save fails
                }
                // If saved successfully, isDirty is now false.
                console.log("[Editor applyEditorChangesToVisualizer] Changes saved successfully.");
            }

            // 2. Parse and Layout the content (which is now saved or wasn't dirty)
            this.isLoading = true; // Set loading specific to parsing/layout
            await this._parseAndLayoutContent(this.rawContent); // Use internal parser
            this.isLoading = false;

            if (this.error) {
                console.error("[Editor applyEditorChangesToVisualizer] Apply failed: Error during parsing or layout.", this.error);
                ElMessage.error(`应用失败: ${this.error}`);
                // Even if parsing failed, the content *was* saved if it was dirty.
                return false; // Indicate failure due to parse/layout error
            } else {
                console.log("[Editor applyEditorChangesToVisualizer] Apply successful: Editor changes parsed and visualized.");
                ElMessage.success("已应用编辑器更改到可视化界面。");
                // isDirty should be false here
                return true; // Indicate success
            }
        },

        // --- Inter-Store Communication Helpers ---

        /** [NEW] Explicit action to request saving the current file */
        async requestSave(): Promise<boolean> {
            if (!this.hasLoadedFile || !this.currentFileInfo.uuid) {
                console.warn("[Editor requestSave] Save requested but no file is loaded.");
                // Maybe allow saving a new file here? Requires interaction with fileManager.
                // For now, just return false if no file is loaded.
                return false;
            }
            if (!this.isDirty) {
                console.log("[Editor requestSave] No changes detected (isDirty is false). Save skipped.");
                ElMessage.info("内容未更改，无需保存。");
                return true; // Nothing to save is considered success
            }

            console.log(`[Editor requestSave] Requesting save for UUID: ${this.currentFileInfo.uuid}`);
            const fileManager = useFileManagerStore();
            // Call fileManagerStore to handle the actual DB update
            const success = await fileManager.saveEditorContent(this.currentFileInfo.uuid, this.rawContent);

            // fileManagerStore handles success/error messages and notifies back via notifySaveSuccess/Failure
            return success;
        },

        /** [NEW] Called by fileManagerStore after successful save */
        notifySaveSuccess(uuid: string, timestamp: Date) {
            if (this.currentFileInfo.uuid === uuid) {
                this.isDirty = false;
                this.currentFileInfo.lastModified = timestamp; // Update timestamp
                this.error = null; // Clear previous save errors
                console.log(`[Editor notifySaveSuccess] Save confirmed for ${uuid}. isDirty reset.`);
            } else {
                console.warn(`[Editor notifySaveSuccess] Received success confirmation for ${uuid}, but current file is ${this.currentFileInfo.uuid}.`);
            }
        },

        /** [NEW] Called by fileManagerStore after failed save */
        notifySaveFailure(uuid: string) {
            if (this.currentFileInfo.uuid === uuid) {
                // Keep isDirty = true
                // Error message should be set by fileManagerStore
                console.error(`[Editor notifySaveFailure] Save failed for ${uuid}. isDirty remains true.`);
            } else {
                console.warn(`[Editor notifySaveFailure] Received failure confirmation for ${uuid}, but current file is ${this.currentFileInfo.uuid}.`);
            }
        },

        // --- Layout Helper ---
        /** Helper to trigger reactivity and optionally update layout */
        _triggerReactivityAndUpdateLayout(forceLayout: boolean = false) {
            this.flowElements.nodes = [...this.flowElements.nodes];
            this.flowElements.edges = [...this.flowElements.edges];

            if (forceLayout) {
                console.log("[Editor] Recalculating layout...");
                this.isLoading = true; // Show loading during layout
                try {
                    const settingsStore = useSettingsStore();
                    const rankdir = ['TB', 'LR'].includes(settingsStore.dagre.rankdir) ? settingsStore.dagre.rankdir : 'TB';
                    const layoutResult = applyDagreLayout(
                        this.flowElements.nodes, // Layout all nodes
                        this.flowElements.edges, // Use all edges
                        rankdir
                    );
                    const positionMap = new Map(layoutResult.nodes.map(n => [n.id, n.position]));
                    this.flowElements.nodes.forEach(node => {
                        const newPosition = positionMap.get(node.id);
                        if (newPosition) {
                            node.position = newPosition;
                        }
                    });
                    this.flowElements.nodes = [...this.flowElements.nodes]; // Trigger reactivity again after position update
                    console.log("[Editor] Layout recalculated.");
                } catch(e: any) {
                    console.error("[Editor] Error during layout recalculation:", e);
                    this.error = `布局计算失败: ${e.message}`;
                    ElMessage.error(this.error);
                } finally {
                    this.isLoading = false;
                }
            }
        },
    }, // End actions
});